extern crate tokio;
// #[macro_use] extern crate lazy_static;

use std::path::Path;
use std::env::args;
use std::collections::VecDeque;
// use std::string::Pattern;

// use async_std::println;
// use async_std::print;

mod utils;
use tokio::fs;
use tokio::prelude::*;
use tokio::task;
use tokio::runtime::Runtime;
use regex::Regex;
use colored::Colorize;


use utils::Except;

async fn read_to_string<P: AsRef<Path>>(path: P, key:&'static str) -> io::Result<()> {
    let path = path.as_ref().to_owned();
    let re = Regex::new(key).unwrap();
    
    let mut stdout = tokio::io::stdout();
    // std.write("skip {}", &path.to_str().unwrap().red()).await;
    let out = task::spawn_blocking(move || {
        let mut output:Vec<String> = Vec::new();
        if let Ok(buf) = std::fs::read_to_string(&path){
            for (no,line) in buf.split("\n").enumerate() {
                'one:
                for cap in re.captures_iter(&line){
                    
                    let p = format!("{}{}{}     ",path.to_str().unwrap().on_yellow().bold()," ".on_blue(),format!("{}",no).on_red());
                    let match_string = cap[0].to_string();
                    let output_string = line.to_string().replace(&match_string, &match_string.green().underline().to_string());
                    if line.len() > 140{
                        let po = line.find(&match_string).unwrap();
                        if po > 70{
                            output.push(format!("{}\n..{}..",p,output_string[po-30..po+30].to_string()))
                        }else{
                            output.push(format!("{}\n{}..",p,&output_string[..100].green()));
                        }
                        
                    }else{
                        output.push(format!("{}\n{}",p, output_string));
                    }
                    break 'one;
                }
            }
        }else{
        };
        output
    }).await?;
    for line in out{
        let _ = stdout.write_all(&format!("{}\n",&line).as_bytes()).await;
    }
             
    Ok(())
}


async fn search(entrys:Option<fs::ReadDir>, key:String,f_type:Option<String>) 
// -> Except<Box<dyn Future<Output = Nothing>>> {
    -> Except<()> {

    // println!("Hello, world!");
    let mut wait_pool:VecDeque<fs::ReadDir> = VecDeque::new(); 
    let key:&'static str = Box::leak(key.into_boxed_str());
    let mut dirs = match entrys {
        Some(dirs) => dirs,
        _ =>{
            if let Ok(dirs) = fs::read_dir(".").await{
                dirs
            }else{
                return Ok(());
            }
        }
    };
    let mut stdout = tokio::io::stdout();
    
    'recurity:
    loop{
        while let Some(entry) = dirs.next_entry().await? {
            let path = entry.path();
            if path.is_dir(){
                // recursive search
                let _ = stdout.write_all(format!("in:{}\r", path.to_str().unwrap().bold().yellow()).as_bytes()).await;
                let _ = stdout.flush().await;
                wait_pool.push_back(fs::read_dir(&path).await?);
            }else{
                if let Some(f_tp) = f_type.clone() {
                    if !path.file_name().unwrap().to_str().unwrap().ends_with(&f_tp) {
                        continue
                    }
                }
                let _ = read_to_string(&path,key).await;
            }
        }
        if wait_pool.len() == 0{break 'recurity}
        if let Some(dirs_t) = wait_pool.pop_front(){
            dirs = dirs_t;
        }else{
            break 'recurity;
        }
    }
    
    
    Ok(())
}

fn main(){
    let mut runtime = Runtime::new().unwrap();
    let key_string:String = args().skip(1).fold("".to_string(), |mut acc,c| {
        if !c.starts_with("-"){
            acc.push_str(" ");
            acc.push_str(&c);
        }    
        acc
    });
    let t_file_tp = args().skip(1)
        .filter(|x| x.starts_with("--"))
        .map(|x| x.replace("--",""))
        .take(1).next();
    println!("search : {}| {:?}",&key_string, t_file_tp);
    runtime.block_on(async {
        loop{
            let _ = search(None, key_string, t_file_tp).await;
            return ;
        }
    });
    println!();
}
